﻿#define v2

#pragma warning disable 1591
#warning 1591 disabled

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Collections;

namespace Jenvin.Library.DBUtility
{
  public class SqlDbUtility : DbUtility, IDisposable
  {

    string _connectionString;
    SqlConnection _connection;

    SqlTransaction _transaction;



    public SqlDbUtility( string connectionString ) : this( connectionString, false ) { }

    public SqlDbUtility( string connectionString, bool hideConnection )
    {
      _connectionString = connectionString;
      HideConnection = hideConnection;
    }

    private SqlConnection CreateConnection()
    {
      if ( _transaction != null )
        throw new NotSupportedException( "在事务中执行时，禁止创建新连接" );

      //这里不能关闭原来的连接，因为它有可能正在被DataReader所使用。
      return _connection = new SqlConnection( _connectionString );
    }

    protected override DbExpressionPaser GetExpressionParser()
    {
      return new SqlCommandParser( null );
    }


    /// <summary>
    /// 执行命令，并返回DataReader对象，请注意数据库连接将在DataReader关闭的同时关闭。
    /// </summary>
    /// <param name="commandText">命令模版</param>
    /// <param name="parameters">参数列表</param>
    /// <returns></returns>
    public SqlDataReader ExecuteReader( string commandText, params object[] parameters )
    {


      IDbCommand command = null;

      try
      {
        return (SqlDataReader) DbUtility.ExecuteReader( command = CreateCommand( commandText, parameters ) );
      }
      catch ( System.Data.Common.DbException e )
      {
        OnError( this, e, command );
        throw;
      }
    }

#if !DotNET10

    /// <summary>
    /// 执行命令，并返回结果集的第一列
    /// </summary>
    /// <typeparam name="T">第一列的数据类型</typeparam>
    /// <param name="commandTemplate">命令模板</param>
    /// <param name="parameters">命令参数</param>
    /// <returns></returns>
    public T[] ExecuteSingleColumn<T>( string commandTemplate, params object[] parameters )
    {
      DataTable data = ExecuteData( commandTemplate, parameters );
      List<T> list = new List<T>();

      foreach ( DataRow row in data.Rows )
        list.Add( (T) row[0] );

      return list.ToArray();
    }
#else
    /// <summary>
    /// 执行命令，并返回结果集的第一列
    /// </summary>
    /// <typeparam name="T">第一列的数据类型</typeparam>
    /// <param name="commandTemplate">命令模板</param>
    /// <param name="parameters">命令参数</param>
    /// <returns></returns>
    public ArrayList ExecuteSingleColumn( string commandTemplate, params object[] parameters )
    {
      DataTable data = ExecuteData( commandTemplate, parameters );
      ArrayList list = new ArrayList();

      foreach ( DataRow row in data.Rows )
        list.Add( row[0] );

      return list;
    }
#endif


    #region DbUtility实现

    protected override IDbCommand CreateCommand( bool createConnection )
    {
      if ( createConnection )
        CreateConnection();

      SqlCommand command = _connection.CreateCommand();

      if ( _transaction != null )
        command.Transaction = _transaction;

      return command;
    }

    protected override IDataAdapter CreateDataAdapter( IDbCommand selectCommand )
    {
      return new SqlDataAdapter( (SqlCommand) selectCommand );
    }

    protected override IDataParameter CreateParameter( string name, object value )
    {
      return new SqlParameter( name, value );
    }

    public static string GetParameterName( string name )
    {
      if ( name.StartsWith( "@" ) )
        return name;
      return "@" + name;
    }

    #endregion


    #region 存储过程支持

    private Dictionary<string, SqlParameter[]> spParametersCollection = new Dictionary<string, SqlParameter[]>();

    /// <summary>
    /// 根据存储过程名称，创建存储过程对象协助执行存储过程。
    /// </summary>
    /// <param name="name">存储过程名</param>
    /// <returns>存储过程对象</returns>
    public SqlStoredProcedure CreateStoredProcedure( string expression, params object[] parameters )
    {
      SqlCommand command = (SqlCommand) CreateCommand( AlwaysCreateConnection );

      string name;
      IDictionary<string, object> parameterDictionary = ParseStoredProcedureExpression( out name, expression, parameters );

      command.CommandText = name;
      command.CommandType = CommandType.StoredProcedure;

      if ( !spParametersCollection.ContainsKey( name ) )
      {
        DeriveParameters( command );

        SqlParameter[] parameterCollection = new SqlParameter[command.Parameters.Count];
        command.Parameters.CopyTo( parameterCollection, 0 );

        spParametersCollection[name] = CloneParameters( parameterCollection );
      }
      else
        command.Parameters.AddRange( CloneParameters( spParametersCollection[name] ) );

      SqlStoredProcedure storedProcedure = new SqlStoredProcedure( command, HideConnection );

      if ( parameterDictionary != null )
      {
        foreach ( KeyValuePair<string, object> pair in parameterDictionary )
          storedProcedure.SetValue( pair.Key, pair.Value );
      }

      return storedProcedure;

    }

    protected override StoredProcedureBase CreateStoredProcedureProtected( string name )
    {
      return CreateStoredProcedure( name );
    }


    private void DeriveParameters( SqlCommand command )
    {
      if ( command.Connection.State == ConnectionState.Closed )
      {
        try
        {
          command.Connection.Open();
          SqlCommandBuilder.DeriveParameters( command );
        }
        finally
        {
          command.Connection.Close();
        }
      }
      else
        SqlCommandBuilder.DeriveParameters( command );
    }

    private SqlParameter[] CloneParameters( SqlParameter[] parameters )
    {
      SqlParameter[] clonedParamters = new SqlParameter[parameters.Length];

      for ( int i = 0; i < parameters.Length; i++ )
        clonedParamters[i] = (SqlParameter) ((ICloneable) parameters[i]).Clone();

      return clonedParamters;
    }

    #endregion


    #region 事务支持

    /// <summary>
    /// 开启新事务
    /// </summary>
    /// <returns>支持事务的SqlDbUtility</returns>
    public SqlDbUtility BeginTransaction()
    {
      SqlConnection connection = new SqlConnection( _connectionString );
      connection.Open();
      return new SqlDbUtility( connection.BeginTransaction(), this );
    }

    /// <summary>
    /// 创建一个事务支持的SqlDbUtility
    /// </summary>
    /// <param name="transaction">事务</param>
    /// <param name="origin">原SqlDbUtility</param>
    private SqlDbUtility( SqlTransaction transaction, SqlDbUtility origin )
    {
      _transaction = transaction;
      _connection = _transaction.Connection;

      AlwaysCreateConnection = false;

      spParametersCollection = origin.spParametersCollection;
    }

    /// <summary>
    /// 提交事务
    /// </summary>
    public void Commit()
    {
      if ( _transaction == null )
        throw new NotSupportedException( "未启用事务" );

      _transaction.Commit();
    }

    /// <summary>
    /// 回滚事务
    /// </summary>
    public void Rollback()
    {
      if ( _transaction == null )
        throw new NotSupportedException( "未启用事务" );

      _transaction.Rollback();
    }

    #endregion


    void IDisposable.Dispose()
    {
      if ( _transaction != null )
        _transaction.Dispose();

      if ( _connection != null )
        _connection.Dispose();
    }
  }
}
